#include "abf.h"

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

typedef struct
{
    const char* name;
    int max_level;
    float score;
} record;

record highscore[] =
{
  { "Ms. John Soda", 4, 23.99 },
  { "Aphex Twin", 17, 19.2 },
};

void put_record(abf_flat_t* flat, const char* name, record* rec)
{
    abf_flat_t* sub = abf_create();
    abf_put_string(sub, "name", rec->name);
    abf_put_sint16(sub, "max_level", rec->max_level);
    abf_put_float(sub, "score", rec->score);
    abf_put_flat(flat, name, sub);
    abf_destroy(sub);
}

void test_record_inorder(abf_flat_t* flat, const char* name, record* rec)
{
    abf_flat_t* sub = abf_get_flat(flat, name);

    /*
    const char * typename = abf_flat_gettypename(abf_flat_gettype(abf_flat_current(sub)));
    printf("%s:%s\n", typename, abf_flat_tostring(abf_flat_current(sub)));
    */
    assert( strcmp(abf_get_string(sub, "name"), rec->name) == 0 );

    /*
    const char * typename = abf_flat_gettypename(abf_flat_gettype(abf_flat_current(sub)));
    printf("%s:%s\n", typename, abf_flat_tostring(abf_flat_current(sub)));
    */
    assert( abf_get_sint16(sub, "max_level") == rec->max_level );

    /*
    const char * typename = abf_flat_gettypename(abf_flat_gettype(abf_flat_current(sub)));
    printf("%s:%s\n", typename, abf_flat_tostring(abf_flat_current(sub)));
    */
    assert( fabs(abf_get_float(sub, "score") - rec->score) < 1.0e-15 );
    abf_destroy (sub);
}

void test_record_outoforder(abf_flat_t* flat, const char* name, record* rec)
{
    abf_flat_t* sub = abf_get_flat(flat, name);
    assert( abf_get_sint16(sub, "max_level") == rec->max_level );
    assert( strcmp(abf_get_string(sub, "name"), rec->name) == 0 );
    assert( fabs(abf_get_float(sub, "score") - rec->score) < 1.0e-15 );
    abf_destroy (sub);
}

void test_mode_switch()
{
    fprintf(stderr, "Testing mode switch ... \n");
    abf_flat_t* flat = abf_create ();
    abf_put_sint8(flat, "multi", 27);
    assert( abf_get_sint8(flat, "multi") == 27 );
    assert( abf_get_sint8(flat, "multi") == 27 );
    abf_put_sint16(flat, "multi-2", 272);
    assert( abf_get_sint8(flat, "multi") == 27 );
    assert( abf_get_sint8(flat, "multi") == 27 );
    assert( abf_get_sint16(flat, "multi-2") == 272 );
    assert( abf_get_sint8(flat, "multi") == 27 );

    assert( abf_ok(flat) );
    abf_destroy(flat);
}

void test_status ()
{
    fprintf(stderr, "Testing error reporting ... \n");
    abf_flat_t* flat = abf_create ();
    assert( abf_ok(flat) );
    assert( abf_getopt_sint8(flat, "multi", 27) == 27);
    assert( abf_ok(flat) );
    abf_get_sint8(flat, "multi");
    assert( abf_getstatus(flat) == ABF_E_NOTFOUND );
    abf_put_sint16(flat, "multi", 27);
    assert( abf_getstatus(flat) == ABF_E_NOTFOUND );
    abf_get_sint8(flat, "multi");
    assert( abf_getstatus(flat) == ABF_E_TYPE_MISMATCH );
    assert( !abf_ok(flat) );
    abf_destroy(flat);
}

void test_record_inout()
{
    abf_flat_t* flat = abf_create ();
    int i;
    int n= sizeof(highscore)/sizeof(highscore[0]);

    for(i=0;i<n;++i)
        put_record(flat, "entry", &highscore[i]);
    fprintf(stderr, "Testing in of order retrieval ... \n");
    abf_flat_seek(flat,0);
    for(i=0;i<n;++i)
        test_record_inorder(flat, "entry", &highscore[i]);

    fprintf(stderr, "Testing out of order retrieval ... \n");
    for(i=0;i<n;++i)
        test_record_outoforder(flat, "entry", &highscore[i]);

    abf_dump(flat, ABF_NOLIMIT);
    abf_destroy(flat);
}

void write_garbage(const char* filename)
{
    const char* garbage = "<?xml version=\"1.0\"?>\n"
"<Data>"
  "<list id=\"all-types\">"
    "<u_int8 id=\"a-uint8\">3</u_int8>"
    "<s_int8 id=\"a-sint8\">-3</u_int8>"
    "</list>"
    "</Data>";

    FILE* f = fopen(filename, "w");
    fprintf (f, "%s", garbage);
    fclose(f);
}

void test_read_nondestructive(const char* format, const char* filename)
{
    uint32_t checksum;
    fprintf (stderr, "Testing non destructive read %s,%s retrieval ... \n", format,filename);
    abf_flat_t* flat = abf_create ();
    abf_put_string (flat, "name", "mikado");
    checksum = abf_flat_checksum(flat);

    assert (ABF_OK != abf_load (abf_getformat (format), flat, filename));
    assert (strcmp(abf_get_string(flat, "name"),"mikado") == 0);
    assert (abf_flat_checksum(flat) == checksum);
    abf_destroy (flat);
}

int main(int argc, char** argv)
{
    const char* filename = "/tmp/abf.test";
    if (argc>1)
        filename = argv[1];
    test_status();
    test_mode_switch();
    test_record_inout();

    write_garbage(filename);
    test_read_nondestructive(ABF_F_ANY,filename);
    test_read_nondestructive(ABF_F_DAT,filename);
    test_read_nondestructive(ABF_F_XML,filename);

    fprintf(stderr, "%s ok\n",argv[0]);
    return 0;
}
